package cn.bonoon.kernel.bytecode.asm;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;

import org.springframework.asm.Label;
import org.springframework.asm.MethodVisitor;
import org.springframework.asm.Opcodes;
import org.springframework.asm.Type;
import org.springframework.context.ApplicationContext;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import cn.bonoon.kernel.IdGenerator;
import cn.bonoon.kernel.annotations.AppendProperty;
import cn.bonoon.kernel.annotations.ArraySeparator;
import cn.bonoon.kernel.annotations.Cipher;
import cn.bonoon.kernel.annotations.CodeBuilder;
import cn.bonoon.kernel.annotations.CodeType;
import cn.bonoon.kernel.annotations.Keyword;
import cn.bonoon.kernel.annotations.MD5Builder;
import cn.bonoon.kernel.annotations.MD5Builder.MD5Type;
import cn.bonoon.kernel.annotations.Tag;
import cn.bonoon.kernel.annotations.Transform;
import cn.bonoon.kernel.annotations.TransformField;
import cn.bonoon.kernel.annotations.TransformFile;
import cn.bonoon.kernel.annotations.Unique;
import cn.bonoon.kernel.annotations.UniqueType;
import cn.bonoon.kernel.annotations.WriteModel;
import cn.bonoon.kernel.bytecode.EntityWriter;
import cn.bonoon.kernel.descriptors.ClassPropertyAccessor;
import cn.bonoon.kernel.descriptors.EntityDescriptor;
import cn.bonoon.kernel.descriptors.EntityPropertyAccessor;
import cn.bonoon.kernel.descriptors.ManyPropertyAccessor;
import cn.bonoon.kernel.descriptors.ObjectDescriptor;
import cn.bonoon.kernel.descriptors.ObjectPropertyAccessor;
import cn.bonoon.kernel.descriptors.OnePropertyAccessor;
import cn.bonoon.kernel.events.OperateEvent;
import cn.bonoon.kernel.exceptions.PropertyParserException;
import cn.bonoon.kernel.io.DirectoryStrategy;
import cn.bonoon.kernel.io.FileInfo;
import cn.bonoon.kernel.io.FileManager;
import cn.bonoon.kernel.io.FilenameStrategy;
import cn.bonoon.kernel.support.CipherGenerator;
import cn.bonoon.kernel.support.CodeGenerator;
import cn.bonoon.kernel.support.TagGenerator;
import cn.bonoon.kernel.support.UniqueChecker;
import cn.bonoon.kernel.support.entities.Persistable;
import cn.bonoon.kernel.util.MD5Util;
import cn.bonoon.kernel.util.StringHelper;

/**
 * <pre>
 * 这里是使用java虚拟机的字节码来直接生成class，由于逻辑非常复杂，所以尽量直接写重复的赋值代码，不过多对这些方法进行抽取
 * 
 * 这个类处理完成后，就应该被释放
 * 
 * 这个类是非常核心的类
 * 
 * 新增处理功能：
 * 1.通过{@link TransformField#writeIgnoreNull()}属性来指定，当普通实体是对象类型，并且字段值为null时，如果处理
 * 相应的数据库实体的字段，如果设为"true"，则忽略掉该赋值语句，数据库实体的字段原来是什么值还是什么值，如果为"false"则把数据库字段设为相应的null值
 * </pre>
 * <pre>
 * 已经完成的转换包括：
 * 
 * 1.把其它类型的数据转换成基本类型的数据，包括基本类型所对应的对象；转换中，日期类型和枚举类型为比较特殊的值，
 * 日期类型转换成基本数据类型，则只能转换成长整形(long或Long)，枚举类型可以转换成整形(int或Integer)；
 * 还可以把枚举类型转换成其它基于整形结构的数据(char/byte/short)，但不能转成这些类型对应的对象类型，一般
 * 不建议使用。
 * 
 * 2.字符串
 * 
 * 3.日期
 * 
 * 4.枚举
 * 
 * 5.生成编码
 * 
 * 6.生成MD5码
 * 
 * 7.标签
 * 
 * 8.唯一值判断
 * 
 * 9.加密解密
 * 
 * 10.embedded子类的情况
 * </pre>
 * @author jackson
 *
 */
public class ASMWriterBuilder extends ASMAbstractConvertBuilder<EntityWriter>{
//	final String eventPath = Type.getInternalName(OperateEvent.class);
	
	private final WriteModel model;
	/** 动态生成的用于内部初始化的方法 */
	private final String initName 	= IdGenerator.build();
	private final String insertName = IdGenerator.build();
	private final String updateName = IdGenerator.build();
	private final String initDes;
	private final String writerDes;
	protected final static int UPDATEONLY = 2;
	protected final static int INSERTONLY = 1;
	protected final static int BOTH = 3;
	protected final String initWriter;
	protected final Map<String, WriterInitProperty> initializeProperties;
	
	public ASMWriterBuilder(Transform transform, ApplicationContext applicationContext, ObjectDescriptor objectDescriptor, EntityDescriptor entityDescriptor){
		super(applicationContext, EntityWriter.class, objectDescriptor, entityDescriptor, OperateEvent.class);
		this.model = transform.write();
		this.initWriter = transform.initWriter();
		String des = "(L" + eventPath + ";L" + managerPath + ";L" + entityPath + ";L" + objectPath + ";)";
		this.writerDes = des + "L" + entityPath + ";";
		this.initDes = des + "V";
		this.initializeProperties = new HashMap<String, WriterInitProperty>(2);
	}

	public WriterInitProperty put(String name, ClassPropertyAccessor initializeProperty) {
		WriterInitProperty ip = initializeProperties.get(name);
		if(ip == null){
			ip = new WriterInitProperty(initializeProperty, null);
			initializeProperties.put(name, ip);
		}
		return ip;
	}
	
	private ASMEvent write_start(String name, String des){
		MethodVisitor writer = visitMethod(ACC_PRIVATE, name, des, null, null);
		writer.visitCode();
		return new ASMEvent(writer, 3, 4, 5);
	}
	
	protected void internalBuild() throws Exception{
		/*
		 * 需要对属性进行预处理，包括：
		 * 1.归类
		 * 2.去掉不可写的属性
		 * 3.只更新的属性
		 * 4.只添加的属性
		 * 
		 * 解析生成转值的处理器，所有的内容都应该是严格处理成功的
		 * 
		 * 使用所有字段的数量来优化临时列表，这样列表是足够用的，不需要动态增加
		 * 处理结束后，列表会被释放，内存应当被回收
		 */
		List<ASMWriterProperty> items = new ArrayList<ASMWriterProperty>(objectDescriptor.size());
		List<ASMWriterProperty> boths = new ArrayList<ASMWriterProperty>(objectDescriptor.size());
		for(ObjectPropertyAccessor objectProperty : objectDescriptor.values()){
			//自动忽略掉使用@AppendField注释的字段
			if(objectProperty.isAnnotationPresent(AppendProperty.class)) continue;
			TransformField transformField = objectProperty.getAnnotation(TransformField.class);
			WriteModel model = null;
			String name;
			boolean ignoreNull = false;
			if(null != transformField) {
				model = transformField.writable();
				if(model == WriteModel.NONE) continue;
				name = transformField.value();
				if("".equals(name)){ 
					name = objectProperty.getName();
				}else if(name.endsWith(".")){
					name += objectProperty.getName();
				}else if(name.startsWith(".")){
					name = objectProperty.getName() + name;
				}else if(name.endsWith(".id")){
					//处理掉以.id结尾的
					//截掉后面的".id"
					name = name.substring(0, name.length() - 3);
				}else if(name.indexOf('[') > 0){
					//这里从一对多或多对多，多的一方取数据的情况，属于只读情况
					continue;
				}
				ignoreNull = transformField.writeIgnoreNull();
			} else { //没有指定，则直接使用普通实体的字段名，其它为默认情况
				name = objectProperty.getName();
			}
			List<ASMWriterProperty> properties = boths;
			EntityPropertyAccessor entityProperty = entityDescriptor.get(name);
			if(null != entityProperty){
				ASMWriterProperty propertyWriter = new ASMWriterProperty(this, name, objectProperty, entityProperty, ignoreNull);
				propertyWriter.init();
				if(model == WriteModel.UPDATEONLY){
					propertyWriter.model = UPDATEONLY;
				}else if(model == WriteModel.INSERTONLY){
					propertyWriter.model = INSERTONLY;
				}
				if(propertyWriter.model != BOTH){ properties = items; }
				if(propertyWriter.priority){
					properties.add(0, propertyWriter);
				}else{
					properties.add(propertyWriter);
				}
			}
			//定义不正确，不需要处理
		}
		
        /*
         * 处理添加和更新的情况
         */
		ASMEvent event = write_start(initName, initDes);
		
		for(WriterInitProperty aip : initializeProperties.values()){
			aip.visitInit(event);
		}
		
		//进行赋值操作
		for (ASMAbstractProperty property : boths) {
			property.parseProperty(event);
		}

		event.writer.visitInsn(RETURN);
		event.writer.visitMaxs(0, 0);
		event.writer.visitEnd();
		
		if(model == WriteModel.WRITABLE){
			insertWriter(items);
			updateWriter(items);
		}else if(model == WriteModel.INSERTONLY){
			//允许添加操作
			insertWriter(items);
		}else if(model == WriteModel.UPDATEONLY){
			updateWriter(items);
		}
        //通知GC回收对象
        boths = null;
        items = null;
	}
	
	private void parseWriter(ASMEvent event)throws Exception{
		//尝试一下进行赋值前调用初始化函数
		try{
			Method init = objectDescriptor.getMethod(initWriter, entityDescriptor.toClass());

			event.writer.visitVarInsn(ALOAD, event.getterPoint);
			event.writer.visitVarInsn(ALOAD, event.setterPoint);
			event.writer.visitMethodInsn(INVOKEVIRTUAL, objectPath, init.getName(), Type.getMethodDescriptor(init), false);
		}catch (Exception e) {
			//没有使用初始化函数的方法
		}
		event.writer.visitVarInsn(ALOAD, 0);
		event.writer.visitVarInsn(ALOAD, 1);
		event.writer.visitVarInsn(ALOAD, 2);
		event.writer.visitVarInsn(ALOAD, event.setterPoint);
		event.writer.visitVarInsn(ALOAD, event.getterPoint);
		event.writer.visitMethodInsn(INVOKEVIRTUAL, classPath, initName, initDes, false);
	}
	
	private void writerReturn(ASMEvent event){
		event.writer.visitVarInsn(ALOAD, event.setterPoint);
		event.writer.visitInsn(ARETURN);
		event.writer.visitMaxs(0, 0);
		event.writer.visitEnd();
	}
	
	private void insertWriter(List<ASMWriterProperty> insertItems)throws Exception{
		MethodVisitor writer = _visitMethod("writeInsert", Object.class, OperateEvent.class, EntityManager.class);
		writer.visitVarInsn(ALOAD, 0);
		writer.visitVarInsn(ALOAD, 2);
		writer.visitVarInsn(ALOAD, 3);
		writer.visitTypeInsn(NEW, entityPath);
		writer.visitInsn(DUP);
		writer.visitMethodInsn(INVOKESPECIAL, entityPath, "<init>", "()V", false); 
		writer.visitVarInsn(ALOAD, 1);
		writer.visitTypeInsn(CHECKCAST, objectPath);
		writer.visitMethodInsn(INVOKEVIRTUAL, classPath, insertName, writerDes, false);
		writer.visitInsn(ARETURN);
		writer.visitMaxs(0, 0);
		writer.visitEnd();
		writer = _visitMethod("writeInsert", Object.class, OperateEvent.class, EntityManager.class, Persistable.class);
		writer.visitVarInsn(ALOAD, 0);
		writer.visitVarInsn(ALOAD, 2);
		writer.visitVarInsn(ALOAD, 3);
		writer.visitVarInsn(ALOAD, 4);
		writer.visitTypeInsn(CHECKCAST, entityPath);
		writer.visitVarInsn(ALOAD, 1);
		writer.visitTypeInsn(CHECKCAST, objectPath);
		writer.visitMethodInsn(INVOKEVIRTUAL, classPath, insertName, writerDes, false);
		writer.visitInsn(ARETURN);
		writer.visitMaxs(0, 0);
		writer.visitEnd();
		ASMEvent event = write_start(insertName, writerDes);
		writer = event.writer;
		if(entityDescriptor.is(EntityDescriptor.BASE)){
			writer.visitVarInsn(ALOAD, event.setterPoint);
			writer.visitVarInsn(ALOAD, 1);
			writer.visitMethodInsn(INVOKEVIRTUAL, eventPath, "getId", "()Ljava/lang/Long;", false);
			writer.visitMethodInsn(INVOKEVIRTUAL, entityPath, "setCreatorId", "(Ljava/lang/Long;)V", false);
			writer.visitVarInsn(ALOAD, event.setterPoint);
			writer.visitVarInsn(ALOAD, 1);
			writer.visitMethodInsn(INVOKEVIRTUAL, eventPath, "toOwnerId", "()J", false);
			writer.visitMethodInsn(INVOKEVIRTUAL, entityPath, "setOwnerId", "(J)V", false);
			writer.visitVarInsn(ALOAD, event.setterPoint);
			writer.visitVarInsn(ALOAD, 1);
			writer.visitMethodInsn(INVOKEVIRTUAL, eventPath, "now", "()Ljava/util/Date;", false);
			writer.visitMethodInsn(INVOKEVIRTUAL, entityPath, "setCreateAt", "(Ljava/util/Date;)V", false);
			writer.visitVarInsn(ALOAD, event.setterPoint);
			writer.visitVarInsn(ALOAD, 1);
			writer.visitMethodInsn(INVOKEVIRTUAL, eventPath, "getUsername", "()Ljava/lang/String;", false);
			writer.visitMethodInsn(INVOKEVIRTUAL, entityPath, "setCreatorName", "(Ljava/lang/String;)V", false);
		} else {
			if(entityDescriptor.get("creatorId") != null) {
				writer.visitVarInsn(ALOAD, event.setterPoint);
				writer.visitVarInsn(ALOAD, 1);
				writer.visitMethodInsn(INVOKEVIRTUAL, eventPath, "getId", "()Ljava/lang/Long;", false);
				writer.visitMethodInsn(INVOKEVIRTUAL, entityPath, "setCreatorId", "(Ljava/lang/Long;)V", false);
			}
			if (entityDescriptor.get("ownerId") != null) {
				writer.visitVarInsn(ALOAD, event.setterPoint);
				writer.visitVarInsn(ALOAD, 1);
				writer.visitMethodInsn(INVOKEVIRTUAL, eventPath, "toOwnerId", "()J", false);
				writer.visitMethodInsn(INVOKEVIRTUAL, entityPath, "setOwnerId", "(J)V", false);
			}
			if (entityDescriptor.get("createAt") != null) {
				writer.visitVarInsn(ALOAD, event.setterPoint);
				writer.visitVarInsn(ALOAD, 1);
				writer.visitMethodInsn(INVOKEVIRTUAL, eventPath, "now", "()Ljava/util/Date;", false);
				writer.visitMethodInsn(INVOKEVIRTUAL, entityPath, "setCreateAt", "(Ljava/util/Date;)V", false);
			}
			if (entityDescriptor.get("creatorName") != null) {
				writer.visitVarInsn(ALOAD, event.setterPoint);
				writer.visitVarInsn(ALOAD, 1);
				writer.visitMethodInsn(INVOKEVIRTUAL, eventPath, "getUsername", "()Ljava/lang/String;", false);
				writer.visitMethodInsn(INVOKEVIRTUAL, entityPath, "setCreatorName", "(Ljava/lang/String;)V", false);
			}
		}
		parseWriter(event);

		//进行赋值操作
		for (ASMWriterProperty property : insertItems) {
			if(property.model == INSERTONLY){
				property.parseProperty(event);
			}
		}
		writerReturn(event);
	}
	
	private void updateWriter(List<ASMWriterProperty> updateItems)throws Exception{
		MethodVisitor writer = _visitMethod("writeUpdate", Object.class, OperateEvent.class, EntityManager.class, Long.class);
		writer.visitVarInsn(ALOAD, 0);
		writer.visitVarInsn(ALOAD, 2);
		writer.visitVarInsn(ALOAD, 3);
		writer.visitVarInsn(ALOAD, 3);
		writer.visitLdcInsn(Type.getType(entityDescriptor.toClass()));
		writer.visitVarInsn(ALOAD, 4);
		//是否对ID进行加密，需要把ID计算回来
		visitMethodFind(writer);
		writer.visitTypeInsn(CHECKCAST, entityPath);
		writer.visitVarInsn(ALOAD, 1);
		writer.visitTypeInsn(CHECKCAST, objectPath);
		writer.visitMethodInsn(INVOKEVIRTUAL, classPath, updateName, writerDes, false);
		writer.visitInsn(ARETURN);
		writer.visitMaxs(0, 0);
		writer.visitEnd();
		writer = _visitMethod("writeUpdate", Object.class, OperateEvent.class, EntityManager.class, Persistable.class);
		writer.visitVarInsn(ALOAD, 0);
		writer.visitVarInsn(ALOAD, 2);
		writer.visitVarInsn(ALOAD, 3);
		writer.visitVarInsn(ALOAD, 4);
		writer.visitTypeInsn(CHECKCAST, entityPath);
		writer.visitVarInsn(ALOAD, 1);
		writer.visitTypeInsn(CHECKCAST, objectPath);
		writer.visitMethodInsn(INVOKEVIRTUAL, classPath, updateName, writerDes, false);
		writer.visitInsn(ARETURN);
		writer.visitMaxs(0, 0);
		writer.visitEnd();
		ASMEvent event = write_start(updateName, writerDes);
		writer = event.writer;
		if(entityDescriptor.is(EntityDescriptor.BASE)){
			writer.visitVarInsn(ALOAD, event.setterPoint);
			writer.visitVarInsn(ALOAD, 1);
			writer.visitMethodInsn(INVOKEVIRTUAL, eventPath, "getId", "()Ljava/lang/Long;", false);
			writer.visitMethodInsn(INVOKEVIRTUAL, entityPath, "setUpdaterId", "(Ljava/lang/Long;)V", false);
			writer.visitVarInsn(ALOAD, event.setterPoint);
			writer.visitVarInsn(ALOAD, 1);
			writer.visitMethodInsn(INVOKEVIRTUAL, eventPath, "now", "()Ljava/util/Date;", false);
			writer.visitMethodInsn(INVOKEVIRTUAL, entityPath, "setUpdateAt", "(Ljava/util/Date;)V", false);
			writer.visitVarInsn(ALOAD, event.setterPoint);
			writer.visitVarInsn(ALOAD, 1);
			writer.visitMethodInsn(INVOKEVIRTUAL, eventPath, "getUsername", "()Ljava/lang/String;", false);
			writer.visitMethodInsn(INVOKEVIRTUAL, entityPath, "setUpdaterName", "(Ljava/lang/String;)V", false);
		} else {
			if(entityDescriptor.get("updaterId") != null) {
				writer.visitVarInsn(ALOAD, event.setterPoint);
				writer.visitVarInsn(ALOAD, 1);
				writer.visitMethodInsn(INVOKEVIRTUAL, eventPath, "getId", "()Ljava/lang/Long;", false);
				writer.visitMethodInsn(INVOKEVIRTUAL, entityPath, "setUpdaterId", "(Ljava/lang/Long;)V", false);
			}
			if (entityDescriptor.get("updateAt") != null) {
				writer.visitVarInsn(ALOAD, event.setterPoint);
				writer.visitVarInsn(ALOAD, 1);
				writer.visitMethodInsn(INVOKEVIRTUAL, eventPath, "now", "()Ljava/util/Date;", false);
				writer.visitMethodInsn(INVOKEVIRTUAL, entityPath, "setUpdateAt", "(Ljava/util/Date;)V", false);
			}
			if (entityDescriptor.get("updaterName") != null) {
				writer.visitVarInsn(ALOAD, event.setterPoint);
				writer.visitVarInsn(ALOAD, 1);
				writer.visitMethodInsn(INVOKEVIRTUAL, eventPath, "getUsername", "()Ljava/lang/String;", false);
				writer.visitMethodInsn(INVOKEVIRTUAL, entityPath, "setUpdaterName", "(Ljava/lang/String;)V", false);
			}
		}
		parseWriter(event);
		//进行赋值操作
		for (ASMWriterProperty property : updateItems) {
			if(property.model == UPDATEONLY){
				property.parseProperty(event);
			}
		}
		writerReturn(event);
	}
	SameParser typeParser;
	PropertyParser defaultParser;
	PropertyParser defaultParser(){
		if(null == defaultParser){
			this.defaultParser = new PrimitiveParser()
				.next(new ObjectParser())
				.next(new WriteStringParser());
		}
		return defaultParser;
	}
	SameParser typeParser(){
		if(null == typeParser) typeParser = new WriteSameParser();
		return typeParser;
	}
}

class ASMWriterProperty extends ASMAbstractProperty{
	final ASMWriterBuilder builder;
	final EntityPropertyAccessor entityProperty;
	ASMWriterProperty(ASMWriterBuilder builder, String name, ObjectPropertyAccessor objectProperty, EntityPropertyAccessor entityProperty, boolean ignoreNull) {
		super(name, objectProperty, ignoreNull);
		this.builder = builder;
		this.entityProperty = entityProperty;
	}
	/**
	 * <pre>
	 * 对于写值的模式（把普通实体的值转换成数据库实体的值）
	 * 默认是更新和添加两种操作模式，值的表示为：
	 * 0.不能添加和更新，即忽略
	 * 1.只添加
	 * 2.只更新
	 * 3.添加和更新
	 * 
	 * 对于读值的模式（从数据库或数据库实体读取值绑定到普通实体）
	 * 读值有直接读值和对象读值两种模式，表示为：
	 * 0.不能读取，即忽略
	 * 1.直接从查询语句把值转为普通实体的值
	 * 2.通过数据库实体把值转为普通实体的值
	 * </pre>
	 */
	int model = ASMWriterBuilder.BOTH;
	/**
	 * 优先处理，是为了让某些检查有抛出异常的处理在前面，才不会导致有些复杂的处理完成后才抛出异常
	 */
	boolean priority = false;//是否优先，如果优先则会被添加在列表的前面
	
	WriterInitProperty visitProperty(EntityPropertyAccessor pre){
		EntityPropertyAccessor next = pre.getEmbeddedProperty();
		if(null == next){
			return builder.put(pre.getMappedName(), pre);
		}
		return visitProperty(next).put(pre.getMappedName(), pre);
	}
	ASMMethod omd;
	WriterInitProperty toPre;
	ASMMethod mappedProperty;
	void init(){
		setterType = entityProperty.getType();
		getterType = objectProperty.getType();
		omd = new ASMMethod(builder.objectPath, objectProperty.getter());
		mappedProperty = new ASMMethod(Type.getInternalName(entityProperty.getOwnerClass()), entityProperty.setter());
		
		EntityPropertyAccessor embeddedProperty = entityProperty.getEmbeddedProperty();
		if(null != embeddedProperty){
			toPre = visitProperty(embeddedProperty);
		}
		internalParser = internalParser();
	}
	PropertyParser internalParser;
	
	public void parseProperty(ASMEvent event) throws Exception{
		internalParser.parse(this, event);
	}
	
	PropertyParser internalParser(){
		//TODO 进行初始化
		if(entityProperty instanceof OnePropertyAccessor){
			//定义的字段为另一个普通的实体
			return new WriteEntityParser((OnePropertyAccessor)entityProperty, objectProperty, builder);
		} else if(entityProperty instanceof ManyPropertyAccessor){
			//只要是集合形式的，都应该交给CollectionParser处理
			return new WriteCollectionParser((ManyPropertyAccessor)entityProperty, objectProperty, builder);
		}
//		String eventPath = builder.eventPath;
		boolean isString = String.class.equals(setterType);
		if(isString){
			//TODO 这里添加对上传文件处理的类
			TransformFile transformFile = objectProperty.getAnnotation(TransformFile.class);
			if(null != transformFile){
				return new WriteFileParser(builder, transformFile);
			}
		
			//1.把值转为字符串的特殊指定的处理优先
			CodeBuilder codeBuilder = objectProperty.getAnnotation(CodeBuilder.class);
			if(null != codeBuilder){
				model = ASMWriterBuilder.INSERTONLY;
				return new WriteCodeParser(codeBuilder, builder.eventPath, builder.entityName());
			}
			MD5Builder md5Builder = objectProperty.getAnnotation(MD5Builder.class);
			if(null != md5Builder){
				if(md5Builder.value() != MD5Type.BOTH){
					model = ASMWriterBuilder.INSERTONLY;
				}
				return new WriteMD5Parser(md5Builder, builder.entityPath(), entityProperty);
			}
			if(getterType.isArray()){
				return new WriteStringArrayParser(objectProperty.getComponentType(), objectProperty.getAnnotation(ArraySeparator.class));
			}else if(Iterable.class.isAssignableFrom(getterType)){
				return new WriteStringIterableParser(objectProperty.getActualType(), objectProperty.getAnnotation(ArraySeparator.class));
			}
		} 
		
		if(getterType.equals(setterType)){
			//2.相同类型之间的转换的第二处理
//			Unique unique = objectProperty.getAnnotation(Unique.class);
//			if(null != unique){
//				return new WriteUniqueParser(unique, eventPath, builder.isBase(), 
//						builder.entityName(), builder.entityPath(), entityProperty.getMappedName());
//			}
			Cipher cipher = objectProperty.getAnnotation(Cipher.class);
			if(null != cipher){
				return new WriteCipherParser(cipher);
			}
			if(isString){
				//两者都是string类型的情况
				Tag tag = objectProperty.getAnnotation(Tag.class);
				if(null != tag){
					//支持对标签的读取
					return new WriteTagParser(tag, builder.eventPath, builder.entityPath(), entityProperty);
				}
			}
			priority = true;
			return builder.typeParser();
		}
		return builder.defaultParser();
	}

	@Override
	protected void loadSetter(ASMEvent event) {
		event.writer.visitVarInsn(ALOAD, event.setterPoint); 
		if(null != toPre) toPre.visitGetter(event.writer);
	}

	@Override
	protected void loadGetter(ASMEvent event) {
		event.writer.visitVarInsn(ALOAD, event.getterPoint); 
	}

	@Override
	protected void visitSetter(MethodVisitor visitor) {
		mappedProperty.visitMethodInsn(visitor, INVOKEVIRTUAL, false);
	}

	@Override
	protected void visitGetter(MethodVisitor visitor) {
		omd.visitMethodInsn(visitor, INVOKEVIRTUAL, false);
	}
}

@SuppressWarnings("serial")
class WriterInitProperty extends HashMap<String, WriterInitProperty> implements Opcodes{
	final WriterInitProperty parent;
	final ClassPropertyAccessor initializeProperty;
	final ASMMethod getter;
	final ASMMethod setter;
	WriterInitProperty(ClassPropertyAccessor initializeProperty, WriterInitProperty parent){
		this.initializeProperty = initializeProperty;
		this.parent = parent;
		String path = Type.getInternalName(initializeProperty.getOwnerClass());
		this.getter = new ASMMethod(path, initializeProperty.getter());
		this.setter = new ASMMethod(path, initializeProperty.setter());
	}
	
	public WriterInitProperty put(String name, ClassPropertyAccessor initializeProperty) {
		WriterInitProperty ip = get(name);
		if(ip == null){
			ip = new WriterInitProperty(initializeProperty, this);
			put(name, ip);
		}
		return ip;
	}
	
	public void visitGetter(MethodVisitor visitor){
		if(null != parent) parent.visitGetter(visitor);
		getter.visitMethodInsn(visitor, INVOKEVIRTUAL, false);
	}
	
	public void visitSetter(ASMEvent event){
		event.writer.visitVarInsn(ALOAD, event.setterPoint);
		if(null != parent) parent.visitGetter(event.writer);
		String path = Type.getInternalName(initializeProperty.getType());
		event.writer.visitTypeInsn(NEW, path);  
		event.writer.visitInsn(DUP);
		event.writer.visitMethodInsn(INVOKESPECIAL, path, "<init>", "()V", false);
		setter.visitMethodInsn(event.writer, INVOKEVIRTUAL, false);
		for(WriterInitProperty aip : values()){
			aip.visitSetter(event);
		}
	}
	
	public void visitInit(ASMEvent event){
		Label ifnull = new Label();
		event.writer.visitVarInsn(ALOAD, event.setterPoint);
		visitGetter(event.writer);
		event.writer.visitJumpInsn(IFNONNULL, ifnull);
		visitSetter(event);
		if(isEmpty()){
			event.writer.visitLabel(ifnull);
		}else{
			Label null_else = new Label();
			event.writer.visitJumpInsn(GOTO, null_else);
			event.writer.visitLabel(ifnull);
			for(WriterInitProperty aip : values()){
				aip.visitInit(event);
			}
			event.writer.visitLabel(null_else);
		}
	}
}	

class BuilderDescriptor{
	final String separator;
	final String builderPath;
	final String appendName;
	final String appendDes;
	BuilderDescriptor(ASMAbstractProperty propertyWriter, ArraySeparator separator, Class<?> type) throws Exception{
		if(null != separator){
			this.separator = separator.value();
		}else{
			this.separator = "|";
		}
		Class<StringBuilder> builderClass = StringBuilder.class;
		builderPath = Type.getInternalName(builderClass);
		appendName = "append";
		Method append;
		try{
			append = builderClass.getDeclaredMethod(appendName, type);
		}catch(Exception ex){
			if(type.isPrimitive()){
				append = builderClass.getDeclaredMethod(appendName, int.class);
			}else{
				append = builderClass.getDeclaredMethod(appendName, Object.class);
			}
		}
		appendDes = Type.getMethodDescriptor(append);
	}
}
// --------------------以下只能往数据库实体写值的转换类
class WriteFileParser implements PropertyParser{
	final TransformFile transformFile;
	
	WriteFileParser(ASMWriterBuilder builder, TransformFile transformFile){
		this.transformFile = transformFile;
		this.builderClassPath = builder.classPath;
		this.eventClassPath = builder.eventPath;
		
		this.fmdes = Type.getInternalName(FileManager.class);
		this.dsdes = Type.getInternalName(DirectoryStrategy.class);
		this.fsdes = Type.getInternalName(FilenameStrategy.class);
		this.mhdes = Type.getInternalName(MultipartHttpServletRequest.class);
		this.fides = Type.getInternalName(FileInfo.class);

		this.gfm = "(L" + eventClassPath + ";Ljava/lang/String;)L" + fmdes + ";";
		this.sfile = "(Ljava/lang/String;L" + dsdes + ";L" + fsdes + ";L" + mhdes + ";Ljava/lang/String;)L" + fides + ";";
		EntityDescriptor entityDescriptor = builder.entityDescriptor;
		this.sizeAccessor = entityDescriptor.get(transformFile.propertySize());
		this.filenameAccessor = entityDescriptor.get(transformFile.propertyFilename());
		this.filesuffixAccessor = entityDescriptor.get(transformFile.propertyFilesuffix());
		this.originalNameAccessor = entityDescriptor.get(transformFile.propertyOriginalName());
	}
	
	final EntityPropertyAccessor sizeAccessor, filenameAccessor, filesuffixAccessor, originalNameAccessor;
	
	final String builderClassPath, eventClassPath;
	
	final String fmdes, gfm, sfile;
	
	final String dsdes, fsdes, mhdes, fides;
	
	@Override
	public void parse(ASMAbstractProperty propertyWriter, ASMEvent event) throws Exception {
		// TODO 读取并保存文件
		MethodVisitor visitor = event.writer;
		int fmPoint = event.assignPoint();//point to filemanager
		int fiPoint = event.assignPoint();//poing to fileinfo
		visitor.visitVarInsn(ALOAD, 0);
		visitor.visitVarInsn(ALOAD, 1);
		visitor.visitLdcInsn(transformFile.fileManager());
		visitor.visitMethodInsn(INVOKEVIRTUAL, builderClassPath, "getFileManager", gfm, false);
		visitor.visitVarInsn(ASTORE, fmPoint);
		
		visitor.visitVarInsn(ALOAD, fmPoint);
		visitor.visitLdcInsn(transformFile.directory());
		visitor.visitFieldInsn(GETSTATIC, dsdes, transformFile.directoryStrategy().name(), 'L' + dsdes + ';');
		visitor.visitFieldInsn(GETSTATIC, fsdes, transformFile.filenameStrategy().name(), 'L' + fsdes + ';');
		visitor.visitVarInsn(ALOAD, 1);
		visitor.visitMethodInsn(INVOKEVIRTUAL, eventClassPath, "getRequest", "()Ljavax/servlet/http/HttpServletRequest;", false);
		visitor.visitTypeInsn(CHECKCAST, mhdes);
		visitor.visitLdcInsn(propertyWriter.name + transformFile.suffixComponent());
		visitor.visitMethodInsn(INVOKEINTERFACE, fmdes, "save", sfile, true);
		visitor.visitVarInsn(ASTORE, fiPoint);
		
		Label ifnull = new Label(), ifelse = new Label();
		visitor.visitVarInsn(ALOAD, fiPoint);
		visitor.visitJumpInsn(IFNULL, ifnull);

		propertyWriter.loadSetter(event);
		visitor.visitVarInsn(ALOAD, fiPoint);
		visitor.visitMethodInsn(INVOKEVIRTUAL, fides, "getRelativePath", "()Ljava/lang/String;", false);
		propertyWriter.visitSetter(visitor);
		
		if(transformFile.deleteOldFile()){
			//要把旧的记录删除
			visitor.visitVarInsn(ALOAD, fmPoint);
			propertyWriter.loadGetter(event);
			propertyWriter.visitGetter(visitor);
			visitor.visitMethodInsn(INVOKEINTERFACE, fmdes, "delete", "(Ljava/lang/String;)Z", true);
			visitor.visitInsn(POP);
		}
		
		if(null != sizeAccessor){
			propertyWriter.loadSetter(event);
			__setter(visitor, sizeAccessor, fiPoint, "getSize", "()J");
		}
		
		if(null != filenameAccessor){
			propertyWriter.loadSetter(event);
			__setter(visitor, filenameAccessor, fiPoint, "setFilename", "()Ljava/lang/String;");
		}
		
		if(null != originalNameAccessor){
			propertyWriter.loadSetter(event);
			__setter(visitor, originalNameAccessor, fiPoint, "getOriginalFilename", "()Ljava/lang/String;");
		}
		
		if(null != filesuffixAccessor){
			propertyWriter.loadSetter(event);
			__setter(visitor, filesuffixAccessor, fiPoint, "getFilesuffix", "()Ljava/lang/String;");
		}
		visitor.visitJumpInsn(GOTO, ifelse);
		visitor.visitLabel(ifnull);
		visitor.visitVarInsn(ALOAD, 1);
		visitor.visitLdcInsn(propertyWriter.name + transformFile.suffixDeletable());
		visitor.visitMethodInsn(INVOKEVIRTUAL, eventClassPath, "is", "(Ljava/lang/String;)Z", false);
		visitor.visitJumpInsn(IFEQ, ifelse);

		if(transformFile.deleteOldFile()){
			//要把旧的记录删除
			visitor.visitVarInsn(ALOAD, fmPoint);
			propertyWriter.loadGetter(event);
			propertyWriter.visitGetter(visitor);
			visitor.visitMethodInsn(INVOKEINTERFACE, fmdes, "delete", "(Ljava/lang/String;)Z", true);
			visitor.visitInsn(POP);
		}
		
//		清空文件在实体的路径
		propertyWriter.loadSetter(event);
		visitor.visitLdcInsn("");
		propertyWriter.visitSetter(visitor);
		
		//根据需要清空
		if(null != sizeAccessor){
			propertyWriter.loadSetter(event);
			__empty(visitor, sizeAccessor);
		}
		
		if(null != filenameAccessor){
			propertyWriter.loadSetter(event);
			__empty(visitor, filenameAccessor);
		}
		
		if(null != originalNameAccessor){
			propertyWriter.loadSetter(event);
			__empty(visitor, originalNameAccessor);
		}
		
		if(null != filesuffixAccessor){
			propertyWriter.loadSetter(event);
			__empty(visitor, filesuffixAccessor);
		}
		visitor.visitLabel(ifelse);
	}
	
	private void __setter(MethodVisitor visitor, EntityPropertyAccessor accessor, int point, String name, String des){
		visitor.visitVarInsn(ALOAD, point);
		visitor.visitMethodInsn(INVOKEVIRTUAL, fides, name, des, false);
		__0(visitor, accessor);
	}
	
	private void __empty(MethodVisitor visitor, EntityPropertyAccessor accessor){
		visitor.visitLdcInsn("");
		__0(visitor, accessor);
	}
	
	private void __0(MethodVisitor visitor, EntityPropertyAccessor accessor) {
		String odes = Type.getInternalName(accessor.getOwnerClass());
		Method setter = accessor.setter();
		String mdes = Type.getMethodDescriptor(setter);
		visitor.visitMethodInsn(INVOKEVIRTUAL, odes, setter.getName(), mdes, false);
	}
}

class WriteStringIterableParser implements PropertyParser{
	final Class<?> objectType;
	final ArraySeparator separator;
	WriteStringIterableParser(Class<?> objectType, ArraySeparator separator){
		if(null == objectType){
			this.objectType = Object.class;
		}else{
			this.objectType = objectType;
		}
		this.separator = separator;
	}

	@Override
	public void parse(ASMAbstractProperty propertyWriter, ASMEvent event) throws Exception {
		MethodVisitor visitor = event.writer;
		String typePath = Type.getInternalName(objectType);
		BuilderDescriptor des = new BuilderDescriptor(propertyWriter, separator, objectType);
		int builderPoint = event.assignPoint(), iteratorPoint = event.assignPoint();
		propertyWriter.loadGetter(event);
		propertyWriter.visitGetter(visitor);
		Label ifnull = new Label();
		visitor.visitJumpInsn(IFNULL, ifnull);
		visitor.visitTypeInsn(NEW, des.builderPath);  
		visitor.visitInsn(DUP);
		visitor.visitMethodInsn(INVOKESPECIAL, des.builderPath, "<init>", "()V", false);
		visitor.visitVarInsn(ASTORE, builderPoint);
		propertyWriter.loadGetter(event);
		propertyWriter.visitGetter(visitor);
		visitor.visitMethodInsn(INVOKEINTERFACE, "java/lang/Iterable", "iterator", "()Ljava/util/Iterator;", true);
		visitor.visitVarInsn(ASTORE, iteratorPoint);
		
		Label start = new Label(), loop = new Label();
		visitor.visitJumpInsn(GOTO, start);
		visitor.visitLabel(loop);
		visitor.visitVarInsn(ALOAD, builderPoint);
		visitor.visitLdcInsn(des.separator);
		visitor.visitMethodInsn(INVOKEVIRTUAL, des.builderPath, des.appendName, "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
		visitor.visitVarInsn(ALOAD, iteratorPoint);
		visitor.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "next", "()Ljava/lang/Object;", true);
		visitor.visitTypeInsn(CHECKCAST, typePath);
		visitor.visitMethodInsn(INVOKEVIRTUAL, des.builderPath, des.appendName, des.appendDes, false);
		visitor.visitInsn(POP);
		visitor.visitLabel(start);
		visitor.visitVarInsn(ALOAD, iteratorPoint);
		visitor.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "hasNext", "()Z", true);
		visitor.visitJumpInsn(IFNE, loop);
		visitor.visitVarInsn(ALOAD, builderPoint);
		visitor.visitLdcInsn(des.separator);
		visitor.visitMethodInsn(INVOKEVIRTUAL, des.builderPath, des.appendName, "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
		visitor.visitInsn(POP);
		propertyWriter.loadSetter(event);
		visitor.visitVarInsn(ALOAD, builderPoint);
		visitor.visitMethodInsn(INVOKEVIRTUAL, des.builderPath, "toString", "()Ljava/lang/String;", false);
		propertyWriter.visitSetter(visitor);
		if(propertyWriter.ignoreNull){
			event.writer.visitLabel(ifnull);
		}else{
			Label ifelse = new Label();
			event.writer.visitJumpInsn(GOTO, ifelse);
			event.writer.visitLabel(ifnull);
			propertyWriter.loadSetter(event);
			event.writer.visitInsn(ACONST_NULL);
			propertyWriter.visitSetter(event.writer);
			event.writer.visitLabel(ifelse);
		}
	}
	
}
class WriteStringArrayParser implements PropertyParser{
	final Class<?> objectType;
	final ArraySeparator separator;
	WriteStringArrayParser(Class<?> objectType, ArraySeparator separator){
		this.objectType = objectType;
		this.separator = separator;
	}
	@Override
	public void parse(ASMAbstractProperty propertyWriter, ASMEvent event) throws Exception {
		MethodVisitor visitor = event.writer;
		//把数组类型的数据组成一个字符串
		BuilderDescriptor des = new BuilderDescriptor(propertyWriter, separator, objectType);
		int builderPoint = event.assignPoint(), arrayPoint = event.assignPoint(), indexPoint = event.assignPoint();
		propertyWriter.loadGetter(event);
		propertyWriter.visitGetter(visitor);
		visitor.visitVarInsn(ASTORE, arrayPoint);
		visitor.visitVarInsn(ALOAD, arrayPoint); 
		Label ifnull = new Label();
		visitor.visitJumpInsn(IFNULL, ifnull);
		visitor.visitTypeInsn(NEW, des.builderPath);  
		visitor.visitInsn(DUP);
		visitor.visitMethodInsn(INVOKESPECIAL, des.builderPath, "<init>", "()V", false);
		visitor.visitVarInsn(ASTORE, builderPoint);
		visitor.visitInsn(ICONST_0);
		visitor.visitVarInsn(ISTORE, indexPoint);
		Label start = new Label();
		Label loop = new Label();
		visitor.visitJumpInsn(GOTO, start);
		visitor.visitLabel(loop);
		visitor.visitVarInsn(ALOAD, builderPoint);
		visitor.visitLdcInsn(des.separator);
		visitor.visitMethodInsn(INVOKEVIRTUAL, des.builderPath, des.appendName, "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
		visitor.visitVarInsn(ALOAD, arrayPoint); 
		visitor.visitVarInsn(ILOAD, indexPoint); 
		if(objectType.isPrimitive()){
			if(int.class.equals(objectType)){
				visitor.visitInsn(IALOAD);
			}else if(long.class.equals(objectType)){
				visitor.visitInsn(LALOAD);
			}else if(double.class.equals(objectType)){
				visitor.visitInsn(DALOAD);
			}else if(char.class.equals(objectType)){
				visitor.visitInsn(CALOAD);
			}else if(float.class.equals(objectType)){
				visitor.visitInsn(FALOAD);
			}else if(short.class.equals(objectType)){
				visitor.visitInsn(SALOAD);
			}else{//boolean and byte 都是用 BALOAD
				visitor.visitInsn(BALOAD);
			}
		}else{
			visitor.visitInsn(AALOAD);
		}
		visitor.visitMethodInsn(INVOKEVIRTUAL, des.builderPath, des.appendName, des.appendDes, false);
		visitor.visitInsn(POP);
		//iinc
		visitor.visitIincInsn(indexPoint, 1);
		visitor.visitLabel(start);
		visitor.visitVarInsn(ILOAD, indexPoint); 
		visitor.visitVarInsn(ALOAD, arrayPoint); 
		visitor.visitInsn(ARRAYLENGTH);
		visitor.visitJumpInsn(IF_ICMPLT, loop);
		visitor.visitVarInsn(ALOAD, builderPoint);
		visitor.visitLdcInsn(des.separator);
		visitor.visitMethodInsn(INVOKEVIRTUAL, des.builderPath, des.appendName, "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
		visitor.visitInsn(POP);
		propertyWriter.loadSetter(event);
		visitor.visitVarInsn(ALOAD, builderPoint);
		visitor.visitMethodInsn(INVOKEVIRTUAL, des.builderPath, "toString", "()Ljava/lang/String;", false);
		propertyWriter.visitSetter(visitor);
		if(propertyWriter.ignoreNull){
			event.writer.visitLabel(ifnull);
		}else{
			Label ifelse = new Label();
			event.writer.visitJumpInsn(GOTO, ifelse);
			event.writer.visitLabel(ifnull);
			propertyWriter.loadSetter(event);
			event.writer.visitInsn(ACONST_NULL);
			propertyWriter.visitSetter(event.writer);
			event.writer.visitLabel(ifelse);
		}
	}
	
}

class WriteCollectionParser implements PropertyParser{
	final boolean itf;
	final int INVOKEENTITY;
	final ASMWriterBuilder builder;
	final ManyPropertyAccessor entityProperty;
	final ObjectPropertyAccessor objectProperty;
	WriteCollectionParser(ManyPropertyAccessor entityProperty, ObjectPropertyAccessor objectProperty, ASMWriterBuilder builder){
		this.entityProperty = entityProperty;
		this.objectProperty = objectProperty;
		this.builder = builder;
		if(entityProperty.isInterface()){
			INVOKEENTITY = INVOKEINTERFACE;
			itf = true;
		}else{
			INVOKEENTITY = INVOKEVIRTUAL;
			itf = false;
		}
	}
	
	private Label $if(ASMAbstractProperty propertyWriter, ASMEvent event){
		Label _if = new Label();
		propertyWriter.loadGetter(event);
		propertyWriter.visitGetter(event.writer);
		event.writer.visitJumpInsn(IFNULL, _if);
		return _if;
	}
	
	private void $end_if(ASMAbstractProperty propertyWriter, ASMEvent event, Label _if, ASMMethod getter, ASMMethod empty, ASMMethod remove)throws Exception{
		if(propertyWriter.ignoreNull){
			event.writer.visitLabel(_if);
		}else{
			Label $else = new Label();
			event.writer.visitJumpInsn(GOTO, $else);
			event.writer.visitLabel(_if);
			propertyWriter.loadSetter(event);
			getter.visitMethodInsn(event.writer, INVOKEVIRTUAL, false);
			event.writer.visitJumpInsn(IFNULL, $else);
			//通过foreach删除元素
			Label start = new Label(), loop = new Label();
			event.writer.visitJumpInsn(GOTO, start);
			event.writer.visitLabel(loop);
			propertyWriter.loadSetter(event);
			getter.visitMethodInsn(event.writer, INVOKEVIRTUAL, false);
			event.writer.visitInsn(ICONST_0);
			remove.visitMethodInsn(event.writer, INVOKEENTITY, itf);
			event.writer.visitInsn(POP);
			event.writer.visitLabel(start);
			propertyWriter.loadSetter(event);
			getter.visitMethodInsn(event.writer, INVOKEVIRTUAL, false);
			empty.visitMethodInsn(event.writer, INVOKEENTITY, itf);
			event.writer.visitJumpInsn(IFEQ, loop);
			event.writer.visitLabel($else);
		}
	}
	@Override
	public void parse(ASMAbstractProperty propertyWriter, ASMEvent event) throws Exception {
		Class<?> actualType = entityProperty.getActualType();
		if(Persistable.class.isAssignableFrom(actualType)){
			//先查找列表所使用的类型，如：List，Set等
			String collectionPath;
			if(entityProperty.isList()){
				//使用List等情况
				collectionPath = "java/util/ArrayList";
			}else if(entityProperty.isSet()){
				collectionPath = "java/util/HashSet";
			}else{
				//使用的是ArrayList的子类
				//使用的是HashSet的子类
				collectionPath = Type.getInternalName(entityProperty.getType());
			}
			String entityPropertyPath = Type.getInternalName(entityProperty.getType());
			Type entityPropertyType = Type.getType(actualType);
			Class<?> objectClass = objectProperty.getType();
			ASMMethod entityGetter = new ASMMethod(builder.entityPath, entityProperty.getter());//property.setterGetter();// new MethodDescriptor(property.setterPath, entityProperty.getter());
			ASMMethod remove = new ASMMethod(entityPropertyPath, entityProperty.removeMethod());
			ASMMethod isEmpty = new ASMMethod(entityPropertyPath, entityProperty.emptyMethod());
			//一对多或多对多中多的一方的情况
			if(objectClass.isArray()){
				Class<?> componentType = objectClass.getComponentType();
				if(actualType.isAssignableFrom(componentType)){
					//是数据库实体的子类
					String componentPath = Type.getInternalName(componentType);
					int arrayPoint = event.assignPoint(), indexPoint = event.assignPoint(), itemPoint = event.assignPoint();
					Label start = new Label(), loop = new Label(), loopifnull = new Label(), oldEntity = new Label();
					Label _if = $if(propertyWriter, event);
					$init(propertyWriter, event, collectionPath, entityGetter, remove, isEmpty);
					$for(propertyWriter, event, arrayPoint, indexPoint, start, loop);
					event.writer.visitVarInsn(ALOAD, arrayPoint);
					event.writer.visitVarInsn(ILOAD, indexPoint);
					event.writer.visitInsn(AALOAD);
					event.writer.visitVarInsn(ASTORE, itemPoint);
					event.writer.visitVarInsn(ALOAD, itemPoint);
					event.writer.visitJumpInsn(IFNULL, loopifnull);
					event.writer.visitVarInsn(ALOAD, itemPoint);
					event.writer.visitMethodInsn(INVOKEVIRTUAL, componentPath, "isNew", "()Z", true);
					event.writer.visitJumpInsn(IFEQ, oldEntity);
					event.writer.visitVarInsn(ALOAD, 2);
					event.writer.visitVarInsn(ALOAD, itemPoint);
					builder.visitMethodPersist(event.writer);
					event.writer.visitLabel(oldEntity);
					
					$set_value(propertyWriter, event, entityPropertyPath, entityGetter, itemPoint);
					event.writer.visitLabel(loopifnull);
					$end_for(event, arrayPoint, indexPoint, start, loop);
					$end_if(propertyWriter, event, _if, entityGetter, isEmpty, remove);
				}else if(Long.class.equals(componentType)){		
					int arrayPoint = event.assignPoint(), indexPoint = event.assignPoint(), itemPoint = event.assignPoint();
					Label start = new Label(), loop = new Label(), loopifnull = new Label();
					Label _if = $if(propertyWriter, event);
					$init(propertyWriter, event, collectionPath, entityGetter, remove, isEmpty);
					$for(propertyWriter, event, arrayPoint, indexPoint, start, loop);
					event.writer.visitVarInsn(ALOAD, arrayPoint);
					event.writer.visitVarInsn(ILOAD, indexPoint);
					event.writer.visitInsn(AALOAD);
					event.writer.visitJumpInsn(IFNULL, loopifnull);
					event.writer.visitVarInsn(ALOAD, 2);
					event.writer.visitLdcInsn(entityPropertyType);
					event.writer.visitVarInsn(ALOAD, arrayPoint);
					event.writer.visitVarInsn(ILOAD, indexPoint);
					event.writer.visitInsn(AALOAD);
					builder.visitMethodFind(event.writer);
					event.writer.visitTypeInsn(CHECKCAST, entityPropertyType.getInternalName());
					event.writer.visitVarInsn(ASTORE, itemPoint);
					event.writer.visitVarInsn(ALOAD, itemPoint);
					event.writer.visitJumpInsn(IFNULL, loopifnull);
					
					$set_value(propertyWriter, event, entityPropertyPath, entityGetter, itemPoint);
					event.writer.visitLabel(loopifnull);
					$end_for(event, arrayPoint, indexPoint, start, loop);
					$end_if(propertyWriter, event, _if, entityGetter, isEmpty, remove);
				}else if(long.class.equals(componentType)){
					int arrayPoint = event.assignPoint(), indexPoint = event.assignPoint(), itemPoint = event.assignPoint();
					Label start = new Label(), loop = new Label(), loopifnull = new Label();
					Label _if = $if(propertyWriter, event);
					$init(propertyWriter, event, collectionPath, entityGetter, remove, isEmpty);
					$for(propertyWriter, event, arrayPoint, indexPoint, start, loop);
					event.writer.visitVarInsn(ALOAD, 2);
					event.writer.visitLdcInsn(entityPropertyType);
					event.writer.visitVarInsn(ALOAD, arrayPoint);
					event.writer.visitVarInsn(ILOAD, indexPoint);
					event.writer.visitInsn(LALOAD);
					event.writer.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false);
					builder.visitMethodFind(event.writer);
					event.writer.visitTypeInsn(CHECKCAST, entityPropertyType.getInternalName());
					event.writer.visitVarInsn(ASTORE, itemPoint);
					event.writer.visitVarInsn(ALOAD, itemPoint);
					event.writer.visitJumpInsn(IFNULL, loopifnull);
					
					$set_value(propertyWriter, event, entityPropertyPath, entityGetter, itemPoint);
					event.writer.visitLabel(loopifnull);
					$end_for(event, arrayPoint, indexPoint, start, loop);
					$end_if(propertyWriter, event, _if, entityGetter, isEmpty, remove);
				}
			}else if(Collection.class.isAssignableFrom(objectClass)){
				Class<?> objectActualType = objectProperty.getActualType();
				String objectPropertyPath = Type.getInternalName(objectClass);
				int INVOKEOBJECT;
				boolean oitf;
				if(objectActualType.isInterface()){
					INVOKEOBJECT = INVOKEINTERFACE;
					oitf = true;
				}else{
					INVOKEOBJECT = INVOKEVIRTUAL;
					oitf = false;
				}
				if(actualType.isAssignableFrom(objectActualType)){
					//是数据库实体的子类
					String objectActualPath = Type.getInternalName(objectActualType);
					int iteratorPoint = event.assignPoint(), itemPoint = event.assignPoint();
					Label start = new Label(), loop = new Label(), oldEntity = new Label();
					Label _if = $if(propertyWriter, event);
					$init(propertyWriter, event, collectionPath, entityGetter, remove, isEmpty);
					$while(propertyWriter, event, objectPropertyPath, iteratorPoint, start, loop);
					event.writer.visitVarInsn(ALOAD, iteratorPoint);
					event.writer.visitMethodInsn(INVOKEOBJECT, "java/util/Iterator", "next", "()Ljava/lang/Object;", oitf);
					event.writer.visitTypeInsn(CHECKCAST, entityPropertyType.getInternalName());
					event.writer.visitVarInsn(ASTORE, itemPoint);
					event.writer.visitVarInsn(ALOAD, itemPoint);
					event.writer.visitJumpInsn(IFNULL, start);
					event.writer.visitVarInsn(ALOAD, itemPoint);
					event.writer.visitMethodInsn(INVOKEVIRTUAL, objectActualPath, "isNew", "()Z", false);
					event.writer.visitJumpInsn(IFEQ, oldEntity);
					event.writer.visitVarInsn(ALOAD, 2);
					event.writer.visitVarInsn(ALOAD, itemPoint);
					builder.visitMethodPersist(event.writer);
					event.writer.visitLabel(oldEntity);
					
					$set_value(propertyWriter, event, entityPropertyPath, entityGetter, itemPoint);
					$end_while(event, INVOKEOBJECT, oitf, iteratorPoint, start, loop);
					$end_if(propertyWriter, event, _if, entityGetter, isEmpty, remove);
				}else if(Long.class.equals(objectActualType)){
					//只能是对象，没有基本类型的情况
					int iteratorPoint = event.assignPoint(), idPoint = event.assignPoint(), itemPoint = event.assignPoint();
					Label start = new Label(), loop = new Label();
					Label _if = $if(propertyWriter, event);
					$init(propertyWriter, event, collectionPath, entityGetter, remove, isEmpty);
					$while(propertyWriter, event, objectPropertyPath, iteratorPoint, start, loop);
					event.writer.visitVarInsn(ALOAD, iteratorPoint);
					event.writer.visitMethodInsn(INVOKEOBJECT, "java/util/Iterator", "next", "()Ljava/lang/Object;", oitf);
					event.writer.visitTypeInsn(CHECKCAST, "java/lang/Long");
					event.writer.visitVarInsn(ASTORE, idPoint);
					event.writer.visitVarInsn(ALOAD, idPoint);
					event.writer.visitJumpInsn(IFNULL, start);
					event.writer.visitVarInsn(ALOAD, 2);
					event.writer.visitLdcInsn(entityPropertyType);
					event.writer.visitVarInsn(ALOAD, idPoint);
					builder.visitMethodFind(event.writer);
					event.writer.visitTypeInsn(CHECKCAST, entityPropertyType.getInternalName());
					event.writer.visitVarInsn(ASTORE, itemPoint);
					event.writer.visitVarInsn(ALOAD, itemPoint);
					event.writer.visitJumpInsn(IFNULL, start);
					
					$set_value(propertyWriter, event, entityPropertyPath, entityGetter, itemPoint);
					$end_while(event, INVOKEOBJECT, oitf, iteratorPoint, start, loop);
					$end_if(propertyWriter, event, _if, entityGetter, isEmpty, remove);
				}
			}else if(String.class.equals(objectClass)){
			}
		}else if(String.class.equals(actualType)){
			//TODO 以列表形式存储字符串 目前没有处理这种情况 
		}
	}

	void $set_value(ASMAbstractProperty propertyWriter, ASMEvent event, String entityPath, ASMMethod getter, int itemPoint) {
		propertyWriter.loadSetter(event);
		getter.visitMethodInsn(event.writer, INVOKEVIRTUAL, false);
		event.writer.visitVarInsn(ALOAD, itemPoint);
		event.writer.visitMethodInsn(INVOKEENTITY, entityPath, "add", "(Ljava/lang/Object;)Z", itf);
		event.writer.visitInsn(POP);
	}

	void $end_while(ASMEvent event, int INVOKEOBJECT, boolean oitf, int iteratorPoint, Label start, Label loop) {
		event.writer.visitLabel(start);
		event.writer.visitVarInsn(ALOAD, iteratorPoint);
		event.writer.visitMethodInsn(INVOKEOBJECT, "java/util/Iterator", "hasNext", "()Z", oitf);
		event.writer.visitJumpInsn(IFEQ, loop);
	}

	void $while(ASMAbstractProperty propertyWriter, ASMEvent event, String path, int iteratorPoint, Label start, Label loop) {
		propertyWriter.loadGetter(event);
		propertyWriter.visitGetter(event.writer);
		event.writer.visitMethodInsn(INVOKEINTERFACE, path, "iterator", "()Ljava/util/Iterator;", true);
		event.writer.visitVarInsn(ASTORE, iteratorPoint);
		event.writer.visitJumpInsn(GOTO, start);
		event.writer.visitLabel(loop);
	}

	void $end_for(ASMEvent event, int arrayPoint, int indexPoint, Label start, Label loop) {
		event.writer.visitIincInsn(indexPoint, 1);
		event.writer.visitLabel(start);
		event.writer.visitVarInsn(ILOAD, indexPoint);
		event.writer.visitVarInsn(ALOAD, arrayPoint);
		event.writer.visitInsn(ARRAYLENGTH);
		event.writer.visitJumpInsn(IF_ICMPLT, loop);
	}

	void $for(ASMAbstractProperty propertyWriter, ASMEvent event, int arrayPoint, int indexPoint, Label start, Label loop) {
		propertyWriter.loadGetter(event);
		propertyWriter.visitGetter(event.writer);
		event.writer.visitVarInsn(ASTORE, arrayPoint);
		event.writer.visitInsn(ICONST_0);
		event.writer.visitVarInsn(ISTORE, indexPoint);
		event.writer.visitJumpInsn(GOTO, start);
		event.writer.visitLabel(loop);
	}

	/**
	 * 如果数据库实体该字段的集合为null则创建一个新的对象，如果不为null则清空该集合的数据
	 * @param propertyWriter
	 * @param event
	 * @param path
	 * @param getter
	 * @param remove
	 * @param empty
	 */
	void $init(ASMAbstractProperty propertyWriter, ASMEvent event, String path, ASMMethod getter, ASMMethod remove, ASMMethod empty) {
		propertyWriter.loadSetter(event);
		getter.visitMethodInsn(event.writer, INVOKEVIRTUAL, false);
		Label ifnonnull = new Label();
		event.writer.visitJumpInsn(IFNONNULL, ifnonnull);
		propertyWriter.loadSetter(event);
		event.writer.visitTypeInsn(NEW, path);  
		event.writer.visitInsn(DUP);
		event.writer.visitMethodInsn(INVOKESPECIAL, path, "<init>", "()V", false);
		propertyWriter.visitSetter(event.writer);
		Label $else = new Label();
		event.writer.visitJumpInsn(GOTO, $else);
		Label ifeq = new Label();
		event.writer.visitLabel(ifeq);
		propertyWriter.loadSetter(event);
		getter.visitMethodInsn(event.writer, INVOKEVIRTUAL, false);
		event.writer.visitInsn(ICONST_0);
		remove.visitMethodInsn(event.writer, INVOKEENTITY, itf);
		event.writer.visitInsn(POP);
		event.writer.visitLabel(ifnonnull);
		propertyWriter.loadSetter(event);
		getter.visitMethodInsn(event.writer, INVOKEVIRTUAL, false);
		empty.visitMethodInsn(event.writer, INVOKEENTITY, itf);
		event.writer.visitJumpInsn(IFEQ, ifeq);
		event.writer.visitLabel($else);
	}
	
}
class WriteEntityParser implements PropertyParser{
	final OnePropertyAccessor entityProperty;
	final ObjectPropertyAccessor objectProperty;
	final ASMWriterBuilder builder;
	WriteEntityParser(OnePropertyAccessor entityProperty, ObjectPropertyAccessor objectProperty, ASMWriterBuilder builder){
		this.entityProperty = entityProperty;
		this.objectProperty = objectProperty;
		this.builder = builder;
	}
	@Override
	public void parse(ASMAbstractProperty propertyWriter, ASMEvent event) throws Exception {
		if(!entityProperty.isMaintainer()) throw new PropertyParserException(entityProperty.getName() + "非关联维护方，无法维护关联关系！");
		/*
		 * 只处理普通实体对应的字段类型定义的是"Long"或"long"数据类型的情况
		 */
		MethodVisitor writer = event.writer;
		Class<?> objectClass = objectProperty.getType();
		if(Long.class.equals(objectClass)){
			Type entityType = Type.getType(entityProperty.getType());
			propertyWriter.loadGetter(event);
			propertyWriter.visitGetter(writer);
			Label ifnull = new Label();
			writer.visitJumpInsn(IFNULL, ifnull);
			
			if(propertyWriter.ignoreNull){
				int entityPoint = event.assignPoint();
				writer.visitVarInsn(ALOAD, 2);
				writer.visitLdcInsn(entityType);
				propertyWriter.loadGetter(event);
				propertyWriter.visitGetter(writer);
				builder.visitMethodFind(writer);
				writer.visitTypeInsn(CHECKCAST, entityType.getInternalName());
				writer.visitVarInsn(ASTORE, entityPoint);
				writer.visitVarInsn(ALOAD, entityPoint);
				Label ifentitynull = new Label();
				writer.visitJumpInsn(IFNULL, ifentitynull);
				propertyWriter.loadSetter(event);
				writer.visitVarInsn(ALOAD, entityPoint);
				propertyWriter.visitSetter(writer);
				writer.visitLabel(ifentitynull);
				
				writer.visitLabel(ifnull);
			}else{
				propertyWriter.loadSetter(event);
				writer.visitVarInsn(ALOAD, 2);
				writer.visitLdcInsn(entityType);
				propertyWriter.loadGetter(event);
				propertyWriter.visitGetter(writer);
				builder.visitMethodFind(writer);
				writer.visitTypeInsn(CHECKCAST, entityType.getInternalName());
				propertyWriter.visitSetter(writer);
			
				Label ifelse = new Label();
				writer.visitJumpInsn(GOTO, ifelse);
				writer.visitLabel(ifnull);
				propertyWriter.loadSetter(event);
				writer.visitInsn(ACONST_NULL);
				propertyWriter.visitSetter(writer);
				writer.visitLabel(ifelse);
			}
		}else if(entityProperty.isAssignableFrom(objectClass)){
			//可以是子类，并且需要判断数据库里是否存在 
			int entityPoint = event.assignPoint();
			propertyWriter.loadGetter(event);
			propertyWriter.visitGetter(writer);
			writer.visitVarInsn(ASTORE, entityPoint);
			writer.visitVarInsn(ALOAD, entityPoint);
			Label $if = new Label();
			writer.visitJumpInsn(IFNULL, $if);
			Label $ifnew = new Label();
			writer.visitVarInsn(ALOAD, entityPoint);
			writer.visitMethodInsn(INVOKEVIRTUAL, builder.entityPath, "isNew", "()Z", false);
			writer.visitJumpInsn(IFEQ, $ifnew);
			event.writer.visitVarInsn(ALOAD, 2);
			event.writer.visitVarInsn(ALOAD, entityPoint);
			builder.visitMethodPersist(event.writer);
			writer.visitLabel($ifnew);
			propertyWriter.loadSetter(event);
			writer.visitVarInsn(ALOAD, entityPoint);
			propertyWriter.visitSetter(writer);
			
			if(propertyWriter.ignoreNull){
				writer.visitLabel($if);
			}else{
				Label $else = new Label();
				writer.visitJumpInsn(GOTO, $else);
				writer.visitLabel($if);
				propertyWriter.loadSetter(event);
				writer.visitInsn(ACONST_NULL);
				propertyWriter.visitSetter(writer);
				writer.visitLabel($else);
			}
		}else if(long.class.equals(objectClass)){
			Type entityType = Type.getType(entityProperty.getType());
			propertyWriter.loadSetter(event);
			writer.visitVarInsn(ALOAD, 2);
			writer.visitLdcInsn(entityType);
			propertyWriter.loadGetter(event);
			propertyWriter.visitGetter(writer);
			writer.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false);
			builder.visitMethodFind(writer);
			writer.visitTypeInsn(CHECKCAST, entityType.getInternalName());
			propertyWriter.visitSetter(writer);
		}else if(String.class.equals(objectClass)){
			// TODO 可以考虑把字符串转为Long类型
		}
	}
}
/**
 * 只有向数据库实体写数据时才有效的功能
 * @author jackson
 *
 */
class WriteMD5Parser implements PropertyParser{
	final MD5Builder md5Builder;
	final String entityPath;
	final EntityPropertyAccessor entityProperty;
	WriteMD5Parser(MD5Builder md5Builder, String entityPath, EntityPropertyAccessor entityProperty){
		this.md5Builder = md5Builder;
		this.entityPath = entityPath;
		this.entityProperty = entityProperty;
	}
	@Override
	public void parse(ASMAbstractProperty propertyWriter, ASMEvent event) throws Exception {
		MethodVisitor writer = event.writer;
		if(md5Builder.value() == MD5Type.RANDOM){
			//这里相当于验证一下这个方法是否存在，如果不存在，这时会抛出异常
			ASMMethod random = new ASMMethod(MD5Util.class, "randomMD5String");
			propertyWriter.loadSetter(event);
			random.visitMethodInsn(writer, INVOKESTATIC, false);
			propertyWriter.visitSetter(writer);
		}else{
			Class<?> objectClass = propertyWriter.getterType;
			ASMMethod md5 = new ASMMethod(MD5Util.class, "getMD5", String.class);
			if(objectClass.isPrimitive()){
				propertyWriter.loadSetter(event);
				propertyWriter.loadGetter(event);
				propertyWriter.visitGetter(writer);
				new StringDescriptor(objectClass).visitMethodInsn(writer);
				md5.visitMethodInsn(writer, INVOKESTATIC, false);
				propertyWriter.visitSetter(writer);
			}else{
				Label ifnull = new Label();
				propertyWriter.loadGetter(event);
				propertyWriter.visitGetter(writer);
				writer.visitJumpInsn(IFNULL, ifnull);
				if(String.class.equals(objectClass)){
					propertyWriter.loadGetter(event);
					propertyWriter.visitGetter(writer);
					propertyWriter.loadSetter(event);
					ASMMethod eg = new ASMMethod(entityPath, entityProperty.getter());//property.setterGetter();
					eg.visitMethodInsn(writer, INVOKEVIRTUAL, false);
					writer.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "equals", "(Ljava/lang/Object;)Z", false);
					Label ifne = new Label();
					writer.visitJumpInsn(IFNE, ifne);
					propertyWriter.loadSetter(event);
					propertyWriter.loadGetter(event);
					propertyWriter.visitGetter(writer);
					md5.visitMethodInsn(writer, INVOKESTATIC, false);
					propertyWriter.visitSetter(writer);
					writer.visitLabel(ifne);
				}else{
					propertyWriter.loadSetter(event);
					propertyWriter.loadGetter(event);
					propertyWriter.visitGetter(writer);
					writer.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(objectClass), "toString", "()Ljava/lang/String;", false);
					md5.visitMethodInsn(writer, INVOKESTATIC, false);
					propertyWriter.visitSetter(writer);
				}
				if(propertyWriter.ignoreNull){
					writer.visitLabel(ifnull);
				}else{
					Label ifelse = new Label();
					writer.visitJumpInsn(GOTO, ifelse);
					writer.visitLabel(ifnull);
					propertyWriter.loadSetter(event);
					writer.visitInsn(ACONST_NULL);
					propertyWriter.visitSetter(writer);
					writer.visitLabel(ifelse);
				}
			}
		}
	}
}
/**
 * 只有向数据库实体写数据时才有效的功能
 * @author jackson
 *
 */
class WriteCodeParser implements PropertyParser{
	final CodeBuilder codeBuilder;
	final String eventPath;
	final String entityName;
	WriteCodeParser(CodeBuilder codeBuilder, String eventPath, String entityName){
		this.codeBuilder = codeBuilder;
		this.eventPath = eventPath;
		this.entityName = entityName;
	}
	@Override
	public void parse(ASMAbstractProperty propertyWriter, ASMEvent event) throws Exception {
		MethodVisitor writer = event.writer;
		String md5 = codeBuilder.value();
		if(md5.isEmpty()){
			md5 = entityName;
		}
		md5 = MD5Util.getMD5(md5);
		Class<CodeGenerator> codeClass = CodeGenerator.class;
		Class<CodeType> typeClass = CodeType.class;
		Constructor<CodeGenerator> cons = codeClass.getDeclaredConstructor(EntityManager.class, typeClass, String.class, int.class);
		Method setPrefix = codeClass.getDeclaredMethod("setPrefix", String.class);
		Method setNow = codeClass.getDeclaredMethod("setNow", Date.class);
		Method generate = codeClass.getDeclaredMethod("generate");
		String codePath = Type.getInternalName(codeClass);
		String typePath = Type.getInternalName(typeClass);
		propertyWriter.loadSetter(event);
		writer.visitTypeInsn(NEW, codePath);  
		writer.visitInsn(DUP);  
		writer.visitVarInsn(ALOAD, 2);
		writer.visitFieldInsn(GETSTATIC, typePath, codeBuilder.type().name(), Type.getDescriptor(typeClass));
		writer.visitLdcInsn(md5);
		writer.visitVarInsn(BIPUSH, codeBuilder.len());
		writer.visitMethodInsn(INVOKESPECIAL, codePath, "<init>", Type.getConstructorDescriptor(cons), false);
		writer.visitLdcInsn(codeBuilder.prefix());
		writer.visitMethodInsn(INVOKEVIRTUAL, codePath, setPrefix.getName(), Type.getMethodDescriptor(setPrefix), false);
		writer.visitVarInsn(ALOAD, 1);
		writer.visitMethodInsn(INVOKEVIRTUAL, eventPath, "now", "()Ljava/util/Date;", false);
		writer.visitMethodInsn(INVOKEVIRTUAL, codePath, setNow.getName(), Type.getMethodDescriptor(setNow), false);
		writer.visitMethodInsn(INVOKEVIRTUAL, codePath, generate.getName(), Type.getMethodDescriptor(generate), false);
		propertyWriter.visitSetter(writer);
	}
}
/**
 * 只有向数据库实体写数据时才有效的功能
 * @author jackson
 *
 */
class WriteTagParser implements PropertyParser{
	final Tag tag;
	final String eventPath;
	final String entityPath;
	final EntityPropertyAccessor entityProperty;
	WriteTagParser(Tag tag, String eventPath, String entityPath, EntityPropertyAccessor entityProperty){
		this.tag = tag;
		this.eventPath = eventPath;
		this.entityPath = entityPath;
		this.entityProperty = entityProperty;
	}
	@Override
	public void parse(ASMAbstractProperty propertyWriter, ASMEvent event) throws Exception {
		MethodVisitor writer = event.writer;
		ASMMethod entityGetter = new ASMMethod(entityPath, entityProperty.getter());//property.setterGetter();
		ASMMethod generate = new ASMMethod(TagGenerator.class, "generate", EntityManager.class, Date.class, String.class, String.class, String.class);
		Label ifnull = new Label();
		propertyWriter.loadGetter(event);
		propertyWriter.visitGetter(writer);
		writer.visitJumpInsn(IFNULL, ifnull);

		propertyWriter.loadSetter(event);
		writer.visitTypeInsn(NEW, generate.path);  
		writer.visitInsn(DUP);
		writer.visitMethodInsn(INVOKESPECIAL, generate.path, "<init>", "()V", false);
		writer.visitVarInsn(ALOAD, 2);
		writer.visitVarInsn(ALOAD, 1);
		writer.visitMethodInsn(INVOKEVIRTUAL, eventPath, "now", "()Ljava/util/Date;", false);
		writer.visitLdcInsn(tag.value());
		propertyWriter.loadGetter(event);
		propertyWriter.visitGetter(writer);
		propertyWriter.loadSetter(event);
		entityGetter.visitMethodInsn(writer, INVOKEVIRTUAL, false);
		generate.visitMethodInsn(writer, INVOKEVIRTUAL, false);
		propertyWriter.visitSetter(writer);
		if(propertyWriter.ignoreNull){
			writer.visitLabel(ifnull);
		}else{
			//把该字段set为null
			Label ifelse = new Label();
			writer.visitJumpInsn(GOTO, ifelse);
			writer.visitLabel(ifnull);
			propertyWriter.loadSetter(event);
			writer.visitInsn(ACONST_NULL);
			propertyWriter.visitSetter(writer);
			writer.visitLabel(ifelse);
		}
	}
}

/**
 * 只有向数据库实体写数据时才有效的功能
 * encrypt
 * 
 * @author jackson
 *
 */
class WriteCipherParser implements PropertyParser{
	final Cipher cipher;
	WriteCipherParser(Cipher cipher){
		this.cipher = cipher;
	}
	
	@Override
	public final void parse(ASMAbstractProperty propertyWriter, ASMEvent event) throws Exception {
		MethodVisitor writer = event.writer;
		if(!propertyWriter.setterType.isPrimitive() && propertyWriter.ignoreNull){
			Label ifnull = new Label();
			propertyWriter.loadGetter(event);
			propertyWriter.visitGetter(writer);
			writer.visitJumpInsn(IFNULL, ifnull);
			cipherParser(propertyWriter, event, writer);
			writer.visitLabel(ifnull);
		}else{
			cipherParser(propertyWriter, event, writer);
		}
	}
	
	void cipherParser(ASMAbstractProperty propertyWriter, ASMEvent event, MethodVisitor writer) throws Exception {
		/*
		 * 目前，只能支持几种类型进行加密解密
		 * 1.字符串
		 * 2.整型
		 * 3.长整型
		 * 相应的，解密也只能支持这几种类型
		 * 
		 * 以后可以根据需要，支持更多的数据类型的加密解密
		 */
		propertyWriter.loadSetter(event);
		if(CipherGenerator.support(propertyWriter.setterType)){
			ASMMethod encrypt = new ASMMethod(CipherGenerator.class, "encrypt", propertyWriter.setterType);
			writer.visitTypeInsn(NEW, encrypt.path);  
			writer.visitInsn(DUP);
			writer.visitLdcInsn(cipher.value());
			writer.visitMethodInsn(INVOKESPECIAL, encrypt.path, "<init>", "(J)V", false);
			propertyWriter.loadGetter(event);
			propertyWriter.visitGetter(writer);
			encrypt.visitMethodInsn(writer, INVOKEVIRTUAL, false);
		}else{
			propertyWriter.loadGetter(event);
			propertyWriter.visitGetter(writer);
		}
		propertyWriter.visitSetter(writer);
	}
	
}

/**
* <pre>
* 读跟写同样有效的功能
* 数据库实体为字符串格式，则可以进行更多的转换指定，包括：
* 1.唯一值的判断
* 2.标签的读取
* 3.对字段加密
* </pre>
* @author jackson
*
*/
class WriteStringParser extends AbstractPropertyParser{

	@Override
	boolean internalParse(ASMAbstractProperty propertyWriter, Class<?> propertyType, ASMEvent event) throws Exception{
		if(!String.class.equals(propertyType)) return false;
		MethodVisitor visitor = event.writer;
		/*
		 * 1.日期类型转为字符串
		 * 2.基本数据类型转为字符串
		 * 3.其它类型的对象转字符串
		 */
		Class<?> to = propertyWriter.getterType;
		if(Date.class.equals(to)){
			ASMMethod toString = new ASMMethod(StringHelper.class, "datetime2String", Date.class);
			if(propertyWriter.ignoreNull){
				int datePoint = event.assignPoint();
				propertyWriter.loadGetter(event);
				propertyWriter.visitGetter(visitor);
				toString.visitMethodInsn(visitor, INVOKESTATIC, false);
				visitor.visitVarInsn(ASTORE, datePoint);
				visitor.visitVarInsn(ALOAD, datePoint); 
				Label ifnull = new Label();
				visitor.visitJumpInsn(IFNULL, ifnull);
				propertyWriter.loadSetter(event);
				visitor.visitVarInsn(ALOAD, datePoint); 
				propertyWriter.visitSetter(visitor);
				visitor.visitLabel(ifnull);
			}else{
				propertyWriter.loadSetter(event);
				propertyWriter.loadGetter(event);
				propertyWriter.visitGetter(visitor);
				toString.visitMethodInsn(visitor, INVOKESTATIC, false);
				propertyWriter.visitSetter(visitor);
			}
		}else if(to.isPrimitive()){
			//一般是不会把基本类型在数据库里定义在字符串来存储的，所以这里先判断一下是否为基本类型，就不用具体的去判断每一个基本类型是什么了
			propertyWriter.loadSetter(event);
			propertyWriter.loadGetter(event);
			propertyWriter.visitGetter(visitor);
			new StringDescriptor(to).visitMethodInsn(visitor);
			propertyWriter.visitSetter(visitor);
		} else {
			//不需要考虑这里两个字段的类型是否都为string，因为相同类型的处理不是这个类
			Label ifnull = new Label();
			propertyWriter.loadGetter(event);
			propertyWriter.visitGetter(visitor);
			visitor.visitJumpInsn(IFNULL, ifnull);
			propertyWriter.loadSetter(event);
			propertyWriter.loadGetter(event);
			propertyWriter.visitGetter(visitor);
			visitor.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(to), "toString", "()Ljava/lang/String;", false);
			propertyWriter.visitSetter(visitor);
			$else_end(propertyWriter, event, ifnull);
		}
		return true;
	}
}
class WriteSameParser extends SameParser{
	@Override
	public void parse(ASMAbstractProperty propertyWriter, ASMEvent event) throws Exception {
		Class<?> st = propertyWriter.setterType;
		if(String.class.equals(st)){
			Unique unique = propertyWriter.objectProperty.getAnnotation(Unique.class);
			if(null != unique){
				ASMWriterProperty asmwp = (ASMWriterProperty)propertyWriter;
				ASMWriterBuilder builder = asmwp.builder;
				MethodVisitor writer = event.writer;
				Class<UniqueChecker> ucClass = UniqueChecker.class;
				Constructor<UniqueChecker> con = ucClass.getConstructor(String.class, String.class, String.class);
				String ucPath = Type.getInternalName(ucClass), 
						conDes = Type.getConstructorDescriptor(con),
						ucDes = Type.getDescriptor(ucClass),
						fName = builder.assignField(ucDes),
						classPath = builder.classPath;
				MethodVisitor constructor = builder.constructor;
				constructor.visitVarInsn(ALOAD, 0);
				constructor.visitTypeInsn(NEW, ucPath);  
				constructor.visitInsn(DUP);  
				constructor.visitLdcInsn(builder.entityName());
				constructor.visitLdcInsn(asmwp.entityProperty.getMappedName());
				constructor.visitLdcInsn(unique.value());
				constructor.visitMethodInsn(INVOKESPECIAL, ucPath, "<init>", conDes, false);
				constructor.visitFieldInsn(PUTFIELD, classPath, fName, ucDes);
				Method check;
				if(builder.isBase()){
					UniqueType ut = unique.type();
					if(ut == UniqueType.MYSELF){
						check = ucClass.getDeclaredMethod("check", EntityManager.class, Long.class, String.class, String.class, Object.class);
						writer.visitVarInsn(ALOAD, 0);
						writer.visitFieldInsn(GETFIELD, classPath, fName, ucDes);
						writer.visitVarInsn(ALOAD, 2);
						propertyWriter.loadSetter(event);
						writer.visitMethodInsn(INVOKEVIRTUAL, builder.entityPath, "getId", "()Ljava/lang/Long;", false);
						propertyWriter.loadGetter(event);
						propertyWriter.visitGetter(writer);
						writer.visitLdcInsn("creatorId");
						writer.visitVarInsn(ALOAD, 1);
						writer.visitMethodInsn(INVOKEVIRTUAL, builder.eventPath, "getId", "()Ljava/lang/Long;", false);
						writer.visitMethodInsn(INVOKEVIRTUAL, ucPath, "check", Type.getMethodDescriptor(check), false);
					}else if(ut == UniqueType.OUR){
						check = ucClass.getDeclaredMethod("check", EntityManager.class, Long.class, String.class, String.class, Object.class);
						writer.visitVarInsn(ALOAD, 0);
						writer.visitFieldInsn(GETFIELD, classPath, fName, ucDes);
						writer.visitVarInsn(ALOAD, 2);
						propertyWriter.loadSetter(event);
						writer.visitMethodInsn(INVOKEVIRTUAL, builder.entityPath, "getId", "()Ljava/lang/Long;", false);
						propertyWriter.loadGetter(event);
						propertyWriter.visitGetter(writer);
						writer.visitLdcInsn("ownerId");
						writer.visitVarInsn(ALOAD, 1);
						writer.visitMethodInsn(INVOKEVIRTUAL, builder.eventPath, "getOwnerId", "()Ljava/lang/Long;", false);
						writer.visitMethodInsn(INVOKEVIRTUAL, ucPath, "check", Type.getMethodDescriptor(check), false);
					}else{
						check = ucClass.getDeclaredMethod("check", EntityManager.class, Long.class, String.class);
						writer.visitVarInsn(ALOAD, 0);
						writer.visitFieldInsn(GETFIELD, classPath, fName, ucDes);
						writer.visitVarInsn(ALOAD, 2);
						propertyWriter.loadSetter(event);
						writer.visitMethodInsn(INVOKEVIRTUAL, builder.entityPath, "getId", "()Ljava/lang/Long;", false);
						propertyWriter.loadGetter(event);
						propertyWriter.visitGetter(writer);
						writer.visitMethodInsn(INVOKEVIRTUAL, ucPath, "check", Type.getMethodDescriptor(check), false);
					}
				}else{
					check = ucClass.getDeclaredMethod("check", EntityManager.class, Long.class, String.class);
					writer.visitVarInsn(ALOAD, 0);
					writer.visitFieldInsn(GETFIELD, classPath, fName, ucDes);
					writer.visitVarInsn(ALOAD, 2);
					propertyWriter.loadSetter(event);
					writer.visitMethodInsn(INVOKEVIRTUAL, builder.entityPath, "getId", "()Ljava/lang/Long;", false);
					propertyWriter.loadGetter(event);
					propertyWriter.visitGetter(writer);
					writer.visitMethodInsn(INVOKEVIRTUAL, ucPath, "check", Type.getMethodDescriptor(check), false);
				}
			}
			Keyword keyword = propertyWriter.objectProperty.getAnnotation(Keyword.class);
			if(null != keyword){
				ASMWriterProperty asmwp = (ASMWriterProperty)propertyWriter;
				ASMWriterBuilder builder = asmwp.builder;
				KeywordChecker kChecker = new KeywordChecker(keyword, builder);
				if(kChecker.checkArray()){
					MethodVisitor writer = event.writer;
					propertyWriter.loadGetter(event);
					propertyWriter.visitGetter(writer);
					kChecker.checkArray(writer);
				}
				if(kChecker.checkDB()){
					MethodVisitor writer = event.writer;
					writer.visitVarInsn(ALOAD, 2);
					propertyWriter.loadGetter(event);
					propertyWriter.visitGetter(writer);
					kChecker.checkDB(writer);
				}
			}
		}
		super.parse(propertyWriter, event);
	}
}